package com.hh.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * Excel 工具类
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class ExcelUtil {

    // 默认导出目录（可自定义）
    @Value("${vehicle.export}")//这里通过配置文件配置，然后用spring提供的value注解注入的，也可以直接指定
    private String defaultExportDir;

    /**
     * 导入整张表
     */
    public <T> List<T> importExcel(MultipartFile file, Class<T> clazz) throws IOException {
        if (file.isEmpty()) {
            throw new IllegalArgumentException("上传的文件为空");
        }

        return EasyExcel.read(file.getInputStream())
                .head(clazz)
                .sheet()
                .doReadSync();
    }

    /**
     * 导出单个 Sheet
     */
    public Path exportSingle(String baseName, Sheet<?> sheet) throws IOException {
        String filepath = generateUniqueFileName(baseName);
        EasyExcel.write(filepath, sheet.getClazz()).sheet(sheet.getName()).doWrite(sheet.getData());
        return Path.of(filepath);
    }

    /**
     * 导出多个 Sheet
     */
    public Path exportMulti(String baseName, List<Sheet<?>> sheets) throws IOException {
        String filepath = generateUniqueFileName(baseName);

        // 创建 ExcelWriter
        ExcelWriter writer = EasyExcel.write(filepath).build();
        // 写入每个 sheet
        for (int i = 0; i < sheets.size(); i++) {
            Sheet<?> sheet = sheets.get(i);
            WriteSheet writeSheet = EasyExcel.writerSheet(i, sheet.getName())
                    .head(sheet.getClazz())
                    .build();
            writer.write(sheet.getData(), writeSheet);
        }
        // 手动关闭 ExcelWriter
        writer.finish();
        return Path.of(filepath);
    }

    /**
     * 生成带时间戳的唯一文件名（避免覆盖）
     */
    private String generateUniqueFileName(String baseName) throws IOException {
        // 确保目录存在
        File dir = new File(defaultExportDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        // 使用时间戳和随机数生成唯一文件名
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String randomSuffix = java.util.UUID.randomUUID().toString().substring(0, 4); // 随机后缀，避免极小概率的冲突

        return String.format("%s%s_%s_%s.xlsx", defaultExportDir, baseName, timestamp, randomSuffix);
    }

    /**
     * 网页上的下载导出，只有一个工作表
     *
     * @param fileName  文件名
     * @param clazz     类的字节码文件，行数据的类型
     * @param dataList  导出的数据
     * @param sheetName 工作表名
     * @param response  响应体
     * @throws IOException 异常对象
     */
    public static void writeWeb(String fileName, final Class<?> clazz, List<?> dataList, String sheetName, HttpServletResponse response) throws IOException {
        // 1.指定响应体内容类型
        response.setContentType("application/vnd.ms-excel");
        // 2.指定编码方式
        response.setCharacterEncoding("utf-8");
        // 3.URLEncoder.encode可以防止中文乱码：import java.net.URLEncoder
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        // 4.指定响应标头
        response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName + ".xlsx");
        // 5.获取工作簿对象的输出流
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
        // 6.设置工作表的名称
        if (!StringUtils.hasText(sheetName)) {
            sheetName = "sheet1";
        }
        // 7.指定写用哪个class去写
        WriteSheet writeSheet = EasyExcel.writerSheet(0, sheetName).head(clazz).build();
        // 8.将 dataList 中的数据逐行写入工作表中
        excelWriter.write(dataList, writeSheet);
        // 9.finish关闭流
        excelWriter.finish();
        // 10.关闭流
        response.getOutputStream().close();
    }

    public static <T> void writeExcelList(HttpServletResponse response, List<List<T>> data, String fileName, Class<?> clazz, String sheetName) throws Exception {
        OutputStream out = getOutputStream(fileName, response);
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(out, clazz).excelType(ExcelTypeEnum.XLSX).registerWriteHandler(getDefaultHorizontalCellStyleStrategy());
        ExcelWriter excelWriter = excelWriterBuilder.build();
        ExcelWriterSheetBuilder excelWriterSheetBuilder;
        WriteSheet writeSheet;
        for (int i = 1; i <= data.size(); i++) {
            excelWriterSheetBuilder = new ExcelWriterSheetBuilder(excelWriter);
            excelWriterSheetBuilder.sheetNo(i);
            excelWriterSheetBuilder.sheetName(sheetName + i);
            writeSheet = excelWriterSheetBuilder.build();
            excelWriter.write(data.get(i - 1), writeSheet);
        }
        excelWriter.finish();
        out.close();
    }

    /**
     * 获取默认表头内容的样式
     *
     * @return
     */
    private static HorizontalCellStyleStrategy getDefaultHorizontalCellStyleStrategy() {
        /** 表头样式 **/
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景色（浅灰色）
        // 可以参考：https://www.cnblogs.com/vofill/p/11230387.html
        headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        // 字体大小
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 10);
        headWriteCellStyle.setWriteFont(headWriteFont);
        //设置表头居中对齐
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        /** 内容样式 **/
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 内容字体样式（名称、大小）
        WriteFont contentWriteFont = new WriteFont();
        contentWriteFont.setFontName("宋体");
        contentWriteFont.setFontHeightInPoints((short) 10);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
//    //设置内容垂直居中对齐
//    contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
//    //设置内容水平居中对齐
//    contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 设置边框样式
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        // 头样式与内容样式合并
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

    private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        //  response.setContentType("application/vnd.ms-excel"); // .xls
        // .xlsx
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf8");
        response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName + ".xlsx");
        return response.getOutputStream();
    }

    /**
     * Sheet 数据封装类
     */
    @AllArgsConstructor
    @Data
    public static class Sheet<T> {
        private final String name;
        private final List<T> data;
        private final Class<T> clazz;
    }
}